package uk.ac.cam.djjb2.mygame;

import java.io.DataInput;
import java.io.IOException;

public class ClientReplicaManager implements MessageReceiver {
	private IdManager<ClientReplicaFactory> classManager = new IdManager<ClientReplicaFactory>();
	private IdManager<ClientReplica> idManager = new IdManager<ClientReplica>();
	private ClientReplicationFactory replicationFactory;

	public ClientReplicaManager(ClientNetManager netManager) {
		this(netManager, null);
	}
	public ClientReplicaManager(ClientNetManager netManager, ClientReplicationFactory factory) {
		this.replicationFactory = (factory == null) ? new DefaultClientReplicationFactory() : factory;
		
		netManager.addMessageReceiver(MessageIds.ID_REPLICA_CREATE, this);
		netManager.addMessageReceiver(MessageIds.ID_REPLICA_DESTROY, this);
		netManager.addMessageReceiver(MessageIds.ID_REPLICA_UPDATE, this);
		netManager.addMessageReceiver(MessageIds.ID_REPLICA_CLASS, this);
	}
	
	public ClientReplica getReplica(int id) {
		return idManager.get(id);
	}
	public <T extends ClientReplica> T getReplica(int id, Class<T> type) {
		return idManager.get(id, type);
	}

	@Override
	public void receive(int senderId, int messageId, DataInput message) throws IOException, InvalidMessageException {
		int id;
		Class<? extends ClientReplica> type;
		ClientReplica instance;
		
		try {
			id = message.readInt();
			
			switch (messageId) {
			case MessageIds.ID_REPLICA_CLASS:
				type = Class.forName(message.readUTF()).asSubclass(ClientReplica.class);
				classManager.put(id, replicationFactory.createReplicaFactory(type));
				
				break;
			case MessageIds.ID_REPLICA_CREATE:
				instance = classManager.get(message.readInt()).newInstance(this, id, message);
				idManager.put(id, instance);
				
				break;
			case MessageIds.ID_REPLICA_DESTROY:
				instance = idManager.remove(id);
				if (instance == null)
					throw new ReplicationException("Invalid replica ID!");
				
				instance.destroy(message);
				break;
			case MessageIds.ID_REPLICA_UPDATE:
				instance = idManager.get(id);
				if (instance == null)
					throw new ReplicationException("Invalid replica ID!");
				
				instance.update(message);
				break;
			}
		} catch (IllegalArgumentException | SecurityException
				| ClassNotFoundException | ReplicationException e) {
			throw new InvalidMessageException(e);
		}
	}
}
